
package jgf.beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import jgf.Attribute;
import jgf.IGraph;
import jgf.exceptions.arc.UnexistentArcException;
import jgf.exceptions.node.UnexistentNodeException;

/**
 * This bean is a piece of information about a generic Graph.
 * It manages all the information relevant to a Graph,
 * i.e. The ID, the label, the lists of nodes and arcs,
 * the list of default attributes for nodes and arcs.<br />
 * This class should be totally populated to avoid undesired effects.
 * The list of attributes represented here should correspond with
 * the default list of the graph for arcs (only the key set).
 *
 * @author Marcos Garcia Ortega <a href="mailto:thiamath@gmail.com">&lt;thiamath@gmail.com&gt;</a>
 */
public class GraphBean implements Serializable{

    protected Integer ID;
    protected String label;
    protected Map<Integer, NodeBean> nodes;
	protected Map<Integer, NodeBean> sources;
	protected Map<Integer, NodeBean> sinks;
	protected Map<String, Attribute> nodeDefaultAttributes;
    protected Map<Integer, ArcBean> arcs;
	protected Map<String, Attribute> arcDefaultAttributes;

	/** Default GraphBean constructor. Initializes all the fields. */
    public GraphBean(){
        nodes = new TreeMap<Integer, NodeBean>();
		nodeDefaultAttributes = new TreeMap<String, Attribute>();
        arcs = new TreeMap<Integer, ArcBean>();
		arcDefaultAttributes = new TreeMap<String, Attribute>();
    }

	public GraphBean(IGraph graph){
		this();
		this.ID = graph.getID();
		this.label = graph.getName();
		for (Integer nodeID : graph.getNodeList()) {
			try {
				this.nodes.put(nodeID, graph.selectNode(nodeID));
			} catch (UnexistentNodeException e) {
				//should never be reached
			}
		}
		for (Attribute nodeAttribute : graph.getDefaultNodeAttributes()) {
			this.nodeDefaultAttributes.put(nodeAttribute.getName(), nodeAttribute);
		}
		for (Integer arcID : graph.getArcList()) {
			try {
				this.arcs.put(arcID, graph.selectArc(arcID));
			} catch (UnexistentArcException e) {
				//should never be reached
			}
		}
		for (Attribute arcAttribute : graph.getDefaultArcAttributes()) {
			this.arcDefaultAttributes.put(arcAttribute.getName(), arcAttribute);
		}
	}

	/**
	 * Gets the list of arcs stored on this graph.
	 * @return A List of ArcBean with the arcs of the graph.
	 */
    public List<ArcBean> getArcs() {
		ArrayList<ArcBean> ret = new ArrayList<ArcBean>();

		for (Integer key : arcs.keySet()) {
			ret.add(arcs.get(key));
		}

        return ret;
    }

	/**
	 * Sets the list of arcs to the list passed as parameter.
	 * @param arcs A List of ArcBean with all the arcs of the graph.
	 */
    public void setArcs(List<ArcBean> arcs){
		this.arcs.clear();
        for (ArcBean arc : arcs) {
			this.arcs.put(arc.getId(), arc);
		}
    }

	/**
	 * Inserts a arc into the graph.<br />
	 * Note: The graph ID number of the inserted arc will be overriden
	 * and replaced with this graph ID.
	 * @param arc An ArcBean.
	 */
	public void insertArc(ArcBean arc){
		arc.setGraphId(ID);
		arcs.put(arc.getId(), arc);
	}

	/**
	 * Removes the arc form the graph.<br />
	 * Note: If the arc ID number do not exist (i.e. the arc do not exist)
	 * then this method will do nothing.
	 * @param arcID The ID number of the arc to delete.
	 */
	public void deleteArc(Integer arcID){
		arcs.remove(arcID);
	}

	/**
	 * Returns the selected arc as a ArcBean from the graph.
	 * @param arcID The ID number of the arc to retrieve.
	 * @return An ArcBean with the information of the arc or null if there is
	 *		no arc with that ID.
	 */
	public ArcBean selectArc(Integer arcID){
		ArcBean ret = arcs.get(arcID);
		return ret;
	}

	/**
	 * Updates the arc from this graph with the new arc passed by parameter.
	 * The match between the previous arc and the new one will be made
	 * checking the ID number.
	 * @param newArc The arc to replace the old one.
	 */
	public void updateArc(ArcBean newArc){
		arcs.put(newArc.getId(), newArc);
	}

	/**
	 * Gets the ID number of this graph.
	 * @return An Integer wth the ID number of the graph.
	 */
    public Integer getID() {
        return ID;
    }

	/**
	 * Sets the ID number of this graph.
	 * @param id An Integer wth the ID number of the graph.
	 */
    public void setId(Integer id) {
        this.ID = id;
    }

	/**
	 * Gets the label of this graph.
	 * @return A String with the label of the graph.
	 */
    public String getLabel() {
        return label;
    }

	/**
	 * Sets the label of this graph.
	 * @param label A String with the label of the graph.
	 */
    public void setLabel(String label) {
        this.label = label;
    }

	/**
	 * Gets the list of nodes stored on this graph.
	 * @return A List of NodeBean with the nodes of the graph.
	 */
    public List<NodeBean> getNodes() {
        ArrayList<NodeBean> ret = new ArrayList<NodeBean>();

		for (Integer key : nodes.keySet()) {
			ret.add(nodes.get(key));
		}

        return ret;
    }

	/**
	 * Sets the list of nodes to the list passed as parameter.
	 * @param arcs A List of NodeBean with all the nodes of the graph.
	 */
    public void setNodes(List<NodeBean> nodes) {
        for (NodeBean node : nodes) {
			this.nodes.put(node.getId(), node);
		}
    }

	/**
	 * Set the node as source.
	 * @param node A <code>NodeBean</code> with the ID informed.
	 */
	public void setSource(NodeBean node){
		sources.put(node.getId(), nodes.get(node.getId()));
	}

	/**
	 * Unset the node from source.
	 * @param node A <code>NodeBean</code> with the ID informed.
	 */
	public void unsetSource(NodeBean node){
		sources.remove(node.getId());
	}

	/**
	 * Gets a list with all the nodes that are sources in the graph.
	 * @return A <code>List</code> of <code>NodeBean</code> with the sources.
	 */
	public List<NodeBean> getSourceList(){
		List<NodeBean> ret = new ArrayList<NodeBean>();

		for (Integer key : sources.keySet()) {
			ret.add(sources.get(key));
		}

		return ret;
	}

	/**
	 * Set the node as sink.
	 * @param node A <code>NodeBean</code> with the ID informed.
	 */
	public void setSink(NodeBean node){
		sinks.put(node.getId(), nodes.get(node.getId()));
	}

	/**
	 * Unset the node from sink.
	 * @param node A <code>NodeBean</code> with the ID informed.
	 */
	public void unsetSink(NodeBean node){
		sinks.remove(node.getId());
	}

	public List<NodeBean> getSinkList(){
		List<NodeBean> ret = new ArrayList<NodeBean>();

		for (Integer key : sinks.keySet()) {
			ret.add(sinks.get(key));
		}

		return ret;
	}

	/**
	 * Inserts a node into the graph.<br />
	 * Note: The graph ID number of the inserted node will be overriden
	 * and replaced with this grapg ID.
	 * @param node A NodeBean.
	 */
	public void insertNode(NodeBean node){
		node.setGraphId(ID);
		nodes.put(node.getId(), node);
	}

	/**
	 * Removes the node form the graph.<br />
	 * Note: If the node ID number do not exist (i.e. the node do not exist)
	 * then this method will do nothing.
	 * @param nodeId The ID number of the node to delete.
	 */
	public void deleteNode(Integer nodeID){
		nodes.remove(nodeID);
	}

	/**
	 * Returns the selected node as a NodeBean from the graph.
	 * @param nodeId The ID number of the node to retrieve.
	 * @return A NodeBean with the information of the node.
	 */
	public NodeBean selectNode(Integer nodeID){
		NodeBean ret = nodes.get(nodeID);
		return ret;
	}

	/**
	 * Updates the node from this graph with the new node passed by parameter.
	 * The match between the previous node and the new one will be made
	 * checking the ID number.
	 * @param newNode The node to replace the old one.
	 */
	public void updateNode(NodeBean newNode){
		nodes.put(newNode.getId(), newNode);
	}

	/**
	 * Gets the list of default attributes for the arcs.
	 * @return A <code>List</code> of <code>Attribute</code> with the default
	 *		attributes for the arcs.
	 */
	public List<Attribute> getDefaultArcAttributes(){
		List<Attribute> ret = new ArrayList<Attribute>();

		for (String key : arcDefaultAttributes.keySet()) {
			ret.add(arcDefaultAttributes.get(key));
		}

		return ret;
	}

	/**
	 * Gets the list of default attributes for the nodes.
	 * @return A <code>List</code> of <code>Attribute</code> with the default
	 *		attributes for the nodes.
	 */
	public List<Attribute> getDefaultNodeAttributes(){
		List<Attribute> ret = new ArrayList<Attribute>();

		for (String key : nodeDefaultAttributes.keySet()) {
			ret.add(nodeDefaultAttributes.get(key));
		}

		return ret;
	}

}
